﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Drawing;
using VisualTest.Models;
using System.IO;
using System.Drawing.Imaging;
using VisualTest.Data;
using System.Xml;
using ExtensionMethods;
using gheat;


namespace VisualTest.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult HeatMap()
        {
            return View();
        }

        public ActionResult FusionTableHeatMap()
        {
            return View();
        }

        public ActionResult EarthQuakes()
        {
            /*EarthQuakesDataContext db = new EarthQuakesDataContext();

            // create reader & open file
            StreamReader tr = new StreamReader(@"C:\huge_quakes.txt");

            while (!tr.EndOfStream)
            {
                NOAA_QUAKE q = new NOAA_QUAKE();
                string s = tr.ReadLine();

                int iValue;
                if(String.IsNullOrEmpty(s.Substring(8, 5).Trim())) q.Date = null;
                if (Int32.TryParse(s.Substring(8, 5), out iValue) && iValue >= 1754) q.Date = new DateTime(iValue, 1, 1);
                else q.Date = null;

                q.Catalog = s.Substring(1, 4).Trim();

                if (String.IsNullOrEmpty(s.Substring(49, 4).Trim())) q.Depth = null;
                else q.Depth = Double.Parse(s.Substring(49, 4));

                if (String.IsNullOrEmpty(s.Substring(62, 3).Trim())) q.Magnitude = null;
                else q.Magnitude = Double.Parse(s.Substring(62, 3));

                q.Latitude = s.Substring(32, 7).Trim();
                q.Longitude = s.Substring(40, 8).Trim();
                q.Orgin = s.Substring(29, 2).Trim();

                db.NOAA_QUAKEs.InsertOnSubmit(q);
            }

            db.SubmitChanges();

            // close the stream
            tr.Close();*/

            return View();
        }

        /* jQuery functions */

        public ActionResult FilterQuakes(int? fromYear, int? toYear, int? fromDepth, int? toDepth, int? fromMagni, int? toMagni)
        {
            List<QuakeModel> l = new List<QuakeModel>();

            EarthQuakesDataContext db = new EarthQuakesDataContext();

            l = (from QUAKES q in db.QUAKES
                 where q.Year >= fromYear.Value && q.Year <= toYear &&
                 q.Depth >= fromDepth.Value && q.Depth <= toDepth.Value &&
                 q.Magnitude >= fromMagni.Value && q.Magnitude <= toMagni.Value
                 select new QuakeModel() { 
                     Depth=q.Depth, 
                     Date= new DateTime(Convert.ToInt32(q.Year),Convert.ToInt32(q.Month),Convert.ToInt32(q.Day)).UnixTicks(), 
                     Magnitude=q.Magnitude, 
                     Latitude = q.Latitude, 
                     Longitude = q.Longitude 
                 }).ToList();

            foreach (var q in l)
            {
                q.GenerateImage(Server.MapPath("~/Content/"));
            }

            return Json(l, JsonRequestBehavior.AllowGet);
        }

        static readonly System.Object _locker = new System.Object();


        [AcceptVerbs(HttpVerbs.Get)]
        public FileResult Tile(string colorScheme, int zoom, int x, int y)
        {
            try
            {

                PointManager pm;
                Bitmap image;

                lock (_locker)
                {
                    InMemoryCache c = new InMemoryCache();
                    pm = c.Get("points", () => DataGateway.GetPoints());

                    image = gheat.GHeat.GetTile(pm, colorScheme, zoom, x, y);
                }

                string url = Server.MapPath("~/Content/HeatMaps/" + Guid.NewGuid() + ".png");

                image.Save(url);

                return new FileStreamResult(new FileStream(url, FileMode.Open), "image/png");
            }
            catch (Exception e)
            {
                throw;
            }
        }

        /// <summary>
        /// Returns bounds of the given tile in latutude/longitude using WGS84 datum
        /// </summary>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        public List<Double> TileLatLonBounds(int tx, int ty, int zoom)
        {
            List<Double> bounds = TileBounds(tx, ty, zoom);
            List<Double> min = MetersToLatLon(bounds[0], bounds[1]);
            List<Double> max = MetersToLatLon(bounds[2], bounds[3]);

            return min.Concat(max).ToList();
        }


        /// <summary>
        /// Returns bounds of the given tile in EPSG:900913 coordinates
        /// </summary>
        /// <param name="tx"></param>
        /// <param name="ty"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        private List<Double> TileBounds(double tx, double ty, double zoom)
        {
            List<Double> min = PixelsToMeters(tx * 256, ty * 256, zoom);
            List<Double> max = PixelsToMeters((tx + 1) * 256, (ty + 1) * 256, zoom);
            return min.Concat(max).ToList();
        }

        /// <summary>
        /// Converts pixel coordinates in given zoom level of pyramid to EPSG:900913
        /// </summary>
        /// <param name="px"></param>
        /// <param name="py"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        private List<Double> PixelsToMeters(double px, double py, double zoom)
        {
            double originShift = 2 * Math.PI * 6378137 / 2.0;

            Double res = Resolution(zoom);
            Double mx = px * res - originShift;
            Double my = py * res - originShift;
            List<Double> r = new List<double>();
            r.Add(mx);
            r.Add(my);
            return r;
        }

        /// <summary>
        /// Resolution (meters/pixel) for given zoom level (measured at Equator)
        /// </summary>
        /// <param name="zoom"></param>
        /// <returns></returns>
        private Double Resolution(double zoom)
        {
            Double initialResolution = 2 * Math.PI * 6378137 / 256;

            // return (2 * math.pi * 6378137) / (self.tileSize * 2**zoom)
            return initialResolution / (Math.Pow(2, zoom));
        }


        /// <summary>
        /// Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum"
        /// </summary>
        /// <param name="mx"></param>
        /// <param name="my"></param>
        /// <returns></returns>
        private List<Double> MetersToLatLon(double mx, double my)
        {
            List<Double> r = new List<double>();

            double originShift = 2 * Math.PI * 6378137 / 2.0;

            double lon = (mx / originShift) * 180.0;
            double lat = (my / originShift) * 180.0;

            lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);

            r.Add(lat);
            r.Add(lon);
            return r;
        }
    }
}
